#include "stdinc.h"

#include "DwarfFort/Interface.h"

#include "Main/Logger.h"

#include "GUI/DwarfGUI.h"

#include "Graphics/MapRenderer.h"



#include "Listener.h"
using namespace Main;

void Listener::updateStats(void)
{
	static String currFps = "Current FPS: ";
	static String avgFps = "Average FPS: ";
	static String bestFps = "Best FPS: ";
	static String worstFps = "Worst FPS: ";
	static String tris = "Triangle Count: ";
	static String batches = "Batch Count: ";

	// update stats when necessary
	try {
		OverlayElement* guiAvg = OverlayManager::getSingleton().getOverlayElement("Core/AverageFps");
		OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("Core/CurrFps");
		OverlayElement* guiBest = OverlayManager::getSingleton().getOverlayElement("Core/BestFps");
		OverlayElement* guiWorst = OverlayManager::getSingleton().getOverlayElement("Core/WorstFps");

		const RenderTarget::FrameStats& stats = mWindow->getStatistics();
		mAvgFrameTime = 1.0f/(stats.avgFPS + 1.0f);
		if (mAvgFrameTime > 0.1f) mAvgFrameTime = 0.1f;

		guiAvg->setCaption(avgFps + StringConverter::toString(stats.avgFPS));
		guiCurr->setCaption(currFps + StringConverter::toString(stats.lastFPS));
		guiBest->setCaption(bestFps + StringConverter::toString(stats.bestFPS)
			+" "+StringConverter::toString(stats.bestFrameTime)+" ms");
		guiWorst->setCaption(worstFps + StringConverter::toString(stats.worstFPS)
			+" "+StringConverter::toString(stats.worstFrameTime)+" ms");

		OverlayElement* guiTris = OverlayManager::getSingleton().getOverlayElement("Core/NumTris");
		guiTris->setCaption(tris + StringConverter::toString(stats.triangleCount));

		OverlayElement* guiBatches = OverlayManager::getSingleton().getOverlayElement("Core/NumBatches");
		guiBatches->setCaption(batches + StringConverter::toString(stats.batchCount));

		OverlayElement* guiDbg = OverlayManager::getSingleton().getOverlayElement("Core/DebugText");
		guiDbg->setCaption(mDebugText);
	}
	catch(...) { /* ignore */ } 
}


// Constructor takes a RenderWindow because it uses that to determine input context
Listener::Listener(RenderWindow* win, Camera* cam,GUI::DwarfGUI* gui, Graphics::MapRenderer *mapRenderer,
				   bool bufferedKeys , bool bufferedMouse) :
	mCamera(cam), mTranslateVector(Vector3::ZERO), mWindow(win), mStatsOn(true), mNumScreenShots(0),
	mMoveScale(0.0f), mRotScale(0.0f), mTimeUntilNextToggle(0), mFiltering(TFO_BILINEAR),
	mAniso(1), mSceneDetailIndex(0), mMoveSpeed(100), mRotateSpeed(36), mDebugOverlay(0),
	mInputManager(0), mMouse(0), mKeyboard(0),mGui(gui),mAvgFrameTime(0.1),mLBDown(false),
	mMapRenderer(mapRenderer)
{
	using namespace OIS;

	mDebugOverlay = OverlayManager::getSingleton().getByName("Core/DebugOverlay");
	mDebugOverlay->hide();
	LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
	ParamList pl;
	size_t windowHnd = 0;
	std::ostringstream windowHndStr;

	win->getCustomAttribute("WINDOW", &windowHnd);
	windowHndStr << windowHnd;
	pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
/*pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
*/
	mInputManager = InputManager::createInputSystem( pl );

	//Create all devices (We only catch joystick exceptions here, as, most people have Key/Mouse)
	mKeyboard = static_cast<Keyboard*>(mInputManager->createInputObject( OISKeyboard, bufferedKeys ));
	mMouse = static_cast<Mouse*>(mInputManager->createInputObject( OISMouse, bufferedMouse ));
	

	//Set initial mouse clipping size
	windowResized(mWindow);

	

	//Register as a Window listener
	WindowEventUtilities::addWindowEventListener(mWindow, this);


	  mMoveSpeed = 5000;
     

	  DwarfFort::Interface::getSingleton().setKeyboard(mKeyboard);
	  mMouse->setEventCallback(this);
	  mKeyboard->setEventCallback(this);

	  
}

//Adjust mouse clipping area
void Listener::windowResized(RenderWindow* rw)
{
	unsigned int width, height, depth;
	int left, top;
	rw->getMetrics(width, height, depth, left, top);

	const OIS::MouseState &ms = mMouse->getMouseState();
	ms.width = width;
	ms.height = height;
}

//Unattach OIS before window shutdown (very important under Linux)
void Listener::windowClosed(RenderWindow* rw)
{
	//Only close for window that created OIS (the main window in these demos)
	if( rw == mWindow )
	{
		if( mInputManager )
		{
			mInputManager->destroyInputObject( mMouse );
			mInputManager->destroyInputObject( mKeyboard );


			OIS::InputManager::destroyInputSystem(mInputManager);
			mInputManager = 0;
		}
	}
}

Listener::~Listener()
{
	//Remove ourself as a Window listener
	WindowEventUtilities::removeWindowEventListener(mWindow, this);
	windowClosed(mWindow);
}

bool Listener::processUnbufferedKeyInput(const FrameEvent& evt)
{
	using namespace OIS;
	
//	DwarfFortInterface::getSingleton().SaveOgreKeys(mKeyboard);

	if(mKeyboard->isKeyDown(KC_A))
		mTranslateVector.x = -mMoveScale;	// Move camera left

	if(mKeyboard->isKeyDown(KC_D))
		mTranslateVector.x = mMoveScale;	// Move camera RIGHT

	if(mKeyboard->isKeyDown(KC_UP) || mKeyboard->isKeyDown(KC_W) )
		mTranslateVector.z = -mMoveScale;	// Move camera forward

	if(mKeyboard->isKeyDown(KC_DOWN) || mKeyboard->isKeyDown(KC_S) )
		mTranslateVector.z = mMoveScale;	// Move camera backward

	if(mKeyboard->isKeyDown(KC_PGUP))
		mTranslateVector.y = mMoveScale;	// Move camera up

	if(mKeyboard->isKeyDown(KC_PGDOWN))
		mTranslateVector.y = -mMoveScale;	// Move camera down

	if(mKeyboard->isKeyDown(KC_RIGHT))
		mCamera->yaw(-mRotScale);

	if(mKeyboard->isKeyDown(KC_LEFT))
		mCamera->yaw(mRotScale);

	if( mKeyboard->isKeyDown(KC_ESCAPE) || mKeyboard->isKeyDown(KC_Q) )
		return false;

   	if( mKeyboard->isKeyDown(KC_F) && mTimeUntilNextToggle <= 0 )
	{
		mStatsOn = !mStatsOn;
		showDebugOverlay(mStatsOn);
		mTimeUntilNextToggle = 1;
	}

	if( mKeyboard->isKeyDown(KC_T) && mTimeUntilNextToggle <= 0 )
	{
		switch(mFiltering)
		{
		case TFO_BILINEAR:
			mFiltering = TFO_TRILINEAR;
			mAniso = 1;
			break;
		case TFO_TRILINEAR:
			mFiltering = TFO_ANISOTROPIC;
			mAniso = 8;
			break;
		case TFO_ANISOTROPIC:
			mFiltering = TFO_BILINEAR;
			mAniso = 1;
			break;
		default: break;
		}
		MaterialManager::getSingleton().setDefaultTextureFiltering(mFiltering);
		MaterialManager::getSingleton().setDefaultAnisotropy(mAniso);

		showDebugOverlay(mStatsOn);
		mTimeUntilNextToggle = 1;
	}

	if(mKeyboard->isKeyDown(KC_SYSRQ) && mTimeUntilNextToggle <= 0)
	{
		std::ostringstream ss;
		ss << "screenshot_" << ++mNumScreenShots << ".png";
		mWindow->writeContentsToFile(ss.str());
		mTimeUntilNextToggle = 0.5;
		mDebugText = "Saved: " + ss.str();
	}

	if(mKeyboard->isKeyDown(KC_R) && mTimeUntilNextToggle <=0)
	{
		mSceneDetailIndex = (mSceneDetailIndex+1)%3 ;
		switch(mSceneDetailIndex) {
			case 0 : mCamera->setPolygonMode(PM_SOLID); break;
			case 1 : mCamera->setPolygonMode(PM_WIREFRAME); break;
			case 2 : mCamera->setPolygonMode(PM_POINTS); break;
		}
		mTimeUntilNextToggle = 0.5;
	}

	static bool displayCameraDetails = false;
	if(mKeyboard->isKeyDown(KC_P) && mTimeUntilNextToggle <= 0)
	{
		displayCameraDetails = !displayCameraDetails;
		mTimeUntilNextToggle = 0.5;
		if (!displayCameraDetails)
			mDebugText = "";
	}

	// Print camera details
	if(displayCameraDetails)
		mDebugText = "P: " + StringConverter::toString(mCamera->getDerivedPosition()) +
					 " " + "O: " + StringConverter::toString(mCamera->getDerivedOrientation());

	// Return true to continue rendering
	return true;
}

bool Listener::processUnbufferedMouseInput(const FrameEvent& evt)
{
	using namespace OIS;

	// Rotation factors, may not be used if the second mouse button is pressed
	// 2nd mouse button - slide, otherwise rotate
	const MouseState &ms = mMouse->getMouseState();
	if( ms.buttonDown( MB_Right ) )
	{
		mTranslateVector.x += ms.X.rel * 0.13;
		mTranslateVector.y -= ms.Y.rel * 0.13;
	}
	else
	{
		mRotX = Degree(-ms.X.rel * 0.13);
		mRotY = Degree(-ms.Y.rel * 0.13);
	}

	return true;
}

void Listener::moveCamera()
{
	// Make all the changes to the camera
	// Note that YAW direction is around a fixed axis (freelook style) rather than a natural YAW
	//(e.g. airplane)
	mCamera->yaw(mRotX);
	mCamera->pitch(mRotY);
	mCamera->moveRelative(mTranslateVector);
}

void Listener::showDebugOverlay(bool show)
{
	
	if (mDebugOverlay)
	{
		if (show)
			mDebugOverlay->show();
		else
			mDebugOverlay->hide();
	}
}

// Override frameStarted event to process that (don't care about frameEnded)
bool Listener::frameStarted(const FrameEvent& evt)
{
	using namespace OIS;

	if (mWindow->isClosed())	return false;

	if (!mWindow->isActive()) return true;


	//Need to capture/update each device
	
	

	mKeyboard->capture();
	mMouse->capture();
	 

	
	moveCamera();
	mRotX = mRotY = 0;
	
	//bool quit = DwarfFort::Interface::getSingleton().DwarfFortGameLoop();
	//if (quit) return false;

	mGui->update(evt.timeSinceLastFrame);

	return true;
}


DWORD lastTickCount = 0;

bool Listener::frameEnded(const FrameEvent& evt)
{
	updateStats();
	

	return true;
}
bool Listener::mouseMoved(const OIS::MouseEvent &arg)
{
	
	if (mLBDown)
	{
		mRotX = Ogre::Degree(-arg.state.X.rel * mAvgFrameTime *2);
		mRotY = Ogre::Degree(-arg.state.Y.rel * mAvgFrameTime *2);
		return true;

	}
	return mGui->mouseMoved(arg);

}
bool Listener::mousePressed(const OIS::MouseEvent &arg,OIS::MouseButtonID id)
{
	if (mLBDown) return true;
	bool result = mGui->mousePressed(arg,id);
	if (result) return result;
	if (id == OIS::MB_Left) 
	{
		mLBDown = true;
		mGui->hideCursor();
		return true;
	}
	return false;
}
bool Listener::mouseReleased(const OIS::MouseEvent &arg,OIS::MouseButtonID id)
{
	if (id == OIS::MB_Left && mLBDown) 
	{
		mLBDown = false;
		mGui->showCursor();
		
		return true;
	}
	return mGui->mouseReleased(arg,id);
}



bool Listener::keyPressed(const OIS::KeyEvent &arg)
{
	bool result = mGui->keyPressed(arg);
	if (result) return result;
	if (arg.key == OIS::KC_W ||
		arg.key == OIS::KC_UP)
	{
			mTranslateVector.z = -1;
			return true;
	}
	if (arg.key == OIS::KC_S ||
		arg.key == OIS::KC_DOWN)
	{
			mTranslateVector.z = 1;
			return true;
	}
	if (arg.key == OIS::KC_A ||
		arg.key == OIS::KC_LEFT)
	{
			mTranslateVector.x = -1;
			return true;
	}
	if (arg.key == OIS::KC_D ||
		arg.key == OIS::KC_RIGHT)
	{
			mTranslateVector.x = 1;
			return true;
	}
	return false;
	
}	



bool Listener::keyReleased(const OIS::KeyEvent &arg)
{
	bool result = mGui->keyReleased(arg);
	if (result) return result;
	if (arg.key == OIS::KC_W ||
		arg.key == OIS::KC_UP ||
		arg.key == OIS::KC_S ||
		arg.key == OIS::KC_DOWN)
	{
			mTranslateVector.z = 0;
			return true;
	}
	if (arg.key == OIS::KC_A ||
		arg.key == OIS::KC_LEFT ||
		arg.key == OIS::KC_D ||
		arg.key == OIS::KC_RIGHT)
	{
			mTranslateVector.x = 0;
			return true;
	}
	return false;
}